import multiprocessing
from typing import Any, Callable, Protocol,Union


class Connection(Protocol):
    def send(self, message: Any): ...

    def recv(self) -> Any: ...

    @property
    def closed(self) -> bool: ...

    def close(self): ...

    def __enter__(self) -> 'Connection': ...

    def __exit__(self, exc_type, exc_val, exc_tb): ...


class SubprocessHandle:
    def __init__(self, func: Union[Callable, type], *args, **kwargs):
        self._connection, sub_connection = multiprocessing.Pipe()
        self._subprocess = multiprocessing.Process(target=func,
            args=(sub_connection, *args), kwargs=kwargs, daemon=True)
        self._subprocess.start()

    def __del__(self):
        self.close_and_wait()

    def _recv(self) -> Any:
        return self._connection.recv()

    def _send(self, message: Any):
        self._connection.send(message)

    @property
    def closed(self) -> bool:
        return not self._subprocess.is_alive()

    def close(self):
        if self._subprocess.is_alive():
            self._subprocess.terminate()
        self._connection.close()

    def wait(self):
        if self._subprocess.is_alive():
            self._subprocess.join()

    def close_and_wait(self):
        self.close()
        self.wait()
